﻿// -------------------------------------------------------------------------------------------------------------------------------
// <copyright file="AggregateStopCriterion{T}.cs" company="Exul">
//     Copyright (c) Exul. All rights reserved.
// </copyright>
// <author>
//     Exul
// </author>
// -------------------------------------------------------------------------------------------------------------------------------

namespace ExulLibrary.Mathematics.LinearAlgebra
{
    #region Usings

    using System;
    using System.Collections.Generic;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;

    using ExulLibrary.Exceptions;

    #endregion

    /// <summary>
    ///     Provides methods to determine the end of an iteration process using several conditions.
    /// </summary>
    /// <typeparam name="T">
    ///     The type of data.
    /// </typeparam>
    public sealed class AggregateStopCriterion<T> : IterationsStopCriterion<T>
    {
        #region Fields

        #region private

        /// <summary>
        ///     The criteria.
        /// </summary>
        private readonly IEnumerable<IterationsStopCriterion<T>> criteria;

        #endregion

        #endregion

        #region Constructors and Destructors

        #region public

        /// <summary>
        ///     Initializes a new instance of the <see cref="AggregateStopCriterion{T}"/> class.
        /// </summary>
        /// <param name="criteria">
        ///     The collection of criteria.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="criteria"/> is <see langword="null"/>.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         One of elements of <paramref name="criteria"/> is <see langword="null"/>.
        ///     </para>
        /// </exception>
        /// <exception cref="EmptyCollectionException">
        ///     <paramref name="criteria"/> is empty collection.
        /// </exception>
        [SuppressMessage(
            "Microsoft.Design",
            "CA1006:DoNotNestGenericTypesInMemberSignatures",
            Justification = "The class is intended with generic collection.")]
        public AggregateStopCriterion(IList<IterationsStopCriterion<T>> criteria)
        {
            #if SAFE

            if (criteria == null)
            {
                throw new ArgumentNullException("criteria");
            }

            if (criteria.Count == 0)
            {
                throw new EmptyCollectionException("criteria");
            }

            for (var index = 0; index < criteria.Count; index++)
            {
                if (criteria[index] == null)
                {
                    throw new ArgumentNullException(string.Format(CultureInfo.CurrentCulture, "criteria[{0}]", index));
                }
            }

            #endif

            this.criteria = criteria;
        }

        /// <summary>
        ///     Initializes a new instance of the <see cref="AggregateStopCriterion{T}"/> class.
        /// </summary>
        /// <param name="criteria">
        ///     The collection of criteria.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="criteria"/> is <see langword="null"/>.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         One of elements of <paramref name="criteria"/> is <see langword="null"/>.
        ///     </para>
        /// </exception>
        /// <exception cref="EmptyCollectionException">
        ///     <paramref name="criteria"/> is empty collection.
        /// </exception>
        public AggregateStopCriterion(params IterationsStopCriterion<T>[] criteria)
        {
            #if SAFE

            if (criteria == null)
            {
                throw new ArgumentNullException("criteria");
            }

            if (criteria.Length == 0)
            {
                throw new EmptyCollectionException("criteria");
            }

            for (var index = 0; index < criteria.Length; index++)
            {
                if (criteria[index] == null)
                {
                    throw new ArgumentNullException(string.Format(CultureInfo.CurrentCulture, "criteria[{0}]", index));
                }
            }

            #endif

            this.criteria = criteria;
        }

        #endregion

        #endregion

        #region Overridden Methods

        #region public

        /// <summary>
        ///     Determines whether an iteration process should be stopped.
        /// </summary>
        /// <param name="data">
        ///     The data describing a current state of the iteration process.
        /// </param>
        /// <returns>
        ///     <see langword="true"/> if the iteration process should be stopped; otherwise, <see langword="false"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="data"/> is <see langword="null"/>.
        /// </exception>
        public override bool ShouldStop(IterationData<T> data)
        {
            #if SAFE

            if (data == null)
            {
                throw new ArgumentNullException("data");
            }

            #endif

            var result = false;
            foreach (var criterion in this.criteria)
            {
                result |= criterion.ShouldStop(data);

                // if found that should stop, don't verify other criteria
                if (result)
                {
                    break;
                }
            }

            return result;
        }

        #endregion

        #endregion
    }
}